home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / c / stdio / vfscanf.c < prev    next >
C/C++ Source or Header  |  1992-01-21  |  11KB  |  467 lines

  1. /* 
  2.  * vfscanf.c --
  3.  *
  4.  *    Source code for the "vfscanf" library procedure.
  5.  *
  6.  * Copyright 1988 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /sprite/src/lib/c/stdio/RCS/vfscanf.c,v 1.6 92/01/21 16:27:07 rab Exp $ SPRITE (Berkeley)";
  18. #endif not lint
  19.  
  20. #include <sprite.h>
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <stdlib.h>
  24. #include <varargs.h>
  25.  
  26. #ifndef TRUE
  27. #define TRUE 1
  28. #endif
  29. #ifndef FALSE
  30. #define FALSE 0
  31. #endif
  32.  
  33. /*
  34.  * Maximum number of bytes in allowable ASCII representation of a
  35.  * floating-point number:
  36.  */
  37.  
  38. #define MAX_FLOAT_SIZE 350
  39. /*
  40.  *----------------------------------------------------------------------
  41.  *
  42.  * vfscanf --
  43.  *
  44.  *    This utility routine does all of the real work of scanning
  45.  *    fields under control of a format string.  It is called by
  46.  *    scanf, fscanf, and sscanf.
  47.  *
  48.  * Results:
  49.  *    Values addressed by elements of args are modified to hold
  50.  *    values scanned from stream.  The return value is a count of
  51.  *    the number of fields successfully scanned from stream, or
  52.  *    EOF if no characters could be input from stream.
  53.  *
  54.  * Side effects:
  55.  *    Information is input from stream.
  56.  *
  57.  *----------------------------------------------------------------------
  58.  */
  59.  
  60. int
  61. vfscanf(stream, format, args)
  62.     register FILE *stream;    /* Where to read characters for parsing. */
  63.     register char *format;    /* Contains literal text and format control
  64.                  * sequences indicating how args are to be
  65.                  * scanned.  See the man page for details. */
  66.     va_list args;        /* Addresses of a variable number of arguments
  67.                  * to be modified. */
  68. {
  69.     int suppress;        /* TRUE means scan value but don't actual
  70.                  * modify an element of args. */
  71.     int storeShort;        /* TRUE means store a short value. */
  72.     int storeLong;        /* TRUE means store a long value. */
  73.     int width;            /* Field width. */
  74.     register char formatChar;     /* Current character from format string.
  75.                  * Eventually it ends up holding the format
  76.                  * type (e.g. 'd' for decimal). */
  77.     register int streamChar;    /* Next character from stream. */
  78.     int assignedFields;        /* Counts number of successfully-assigned
  79.                  * fields. */
  80.     int base;            /* Gives base for numbers:  0 means float,
  81.                  * -1 means not a number.
  82.                  */
  83.     int sign;            /* TRUE means negative sign. */
  84.     char buf[MAX_FLOAT_SIZE+1];
  85.                 /* Place to accumulate floating-point
  86.                  * number for processing. */
  87.     register char *ptr = (char *) NIL;
  88.     char *savedPtr, *end, *firstPtr;
  89.  
  90.     assignedFields = 0;
  91.     streamChar = getc(stream);
  92.     if (streamChar == EOF) {
  93.     return(EOF);
  94.     }
  95.  
  96.     /*
  97.      * The main loop is to scan through the characters in format.
  98.      * Anything but a '%' must match the next character from stream.
  99.      * A '%' signals the start of a format field;  the formatting
  100.      * information is parsed, the next value is scanned from the stream
  101.      * and placed in memory, and the loop goes on.
  102.      */
  103.  
  104.     for (formatChar = *format; (formatChar != 0) && (streamChar != EOF);
  105.         format++, formatChar = *format) {
  106.     
  107.     /*
  108.      * A white-space format character matches any number of
  109.      * white-space characters from the stream.
  110.      */
  111.  
  112.     if (isspace(formatChar)) {
  113.         while (isspace(streamChar)) {
  114.         streamChar = getc(stream);
  115.         }
  116.         continue;
  117.     }
  118.  
  119.     /*
  120.      * Any character but % must be matched exactly by the stream.
  121.      */
  122.  
  123.     if (formatChar != '%') {
  124.         if (streamChar != formatChar) {
  125.         break;
  126.         }
  127.         streamChar = getc(stream);
  128.         continue;
  129.     }
  130.  
  131.     /*
  132.      * Parse off the format control fields.
  133.      */
  134.  
  135.     suppress = FALSE;
  136.     storeLong = FALSE;
  137.     storeShort = FALSE;
  138.     width = -1;
  139.     format++;  
  140.     formatChar = *format;
  141.     if (formatChar == '*') {
  142.         suppress = TRUE;
  143.         format++; 
  144.         formatChar = *format;
  145.     }
  146.     if (isdigit(formatChar)) {
  147.         width = strtoul(format, &end, 10);
  148.         format = end;
  149.         formatChar = *format;
  150.     }
  151.     if (formatChar == 'l') {
  152.         storeLong = TRUE;
  153.         format++; 
  154.         formatChar = *format;
  155.     }
  156.     if (formatChar == 'h') {
  157.         storeShort = TRUE;
  158.         format++; 
  159.         formatChar = *format;
  160.     }
  161.  
  162.     /*
  163.      * Skip any leading blanks in the input (except for 'c' format).
  164.      * Also, default the width to infinity, except for 'c' format.
  165.      */
  166.     
  167.     if ((formatChar != 'c') && (formatChar != '[')) {
  168.         while (isspace(streamChar)) {
  169.         streamChar = getc(stream);
  170.         }
  171.     }
  172.     if ((width <= 0) && (formatChar != 'c')) {
  173.         width = 1000000;
  174.     }
  175.  
  176.     /*
  177.      * Check for EOF again after parsing away the white space.
  178.      */
  179.     if (streamChar == EOF) {
  180.         break;
  181.     }
  182.     
  183.     /*
  184.      * Process the conversion character.  For numbers, this just means
  185.      * turning it into a "base" number that indicates how to read in
  186.      * a number.
  187.      */
  188.     
  189.     base = -1;
  190.     switch (formatChar) {
  191.  
  192.         case '%':
  193.         if (streamChar != '%') {
  194.             goto done;
  195.         }
  196.         streamChar = getc(stream);
  197.         break;
  198.         
  199.         case 'D':
  200.         storeShort = FALSE;
  201.         case 'd':
  202.         base = 10;
  203.         break;
  204.         
  205.         case 'O':
  206.         storeShort = FALSE;
  207.         case 'o':
  208.         base = 8;
  209.         break;
  210.         
  211.         case 'X':
  212.         storeShort = FALSE;
  213.         case 'x':
  214.         base = 16;
  215.         break;
  216.         
  217.         case 'E':
  218.         case 'F':
  219.         storeLong = TRUE;
  220.         case 'e':
  221.         case 'f':
  222.         base = 0;
  223.         break;
  224.  
  225.         /*
  226.          * Characters and strings are handled in exactly the same way,
  227.          * except that for characters the default width is 1 and spaces
  228.          * are not considered terminators.
  229.          */
  230.  
  231.         case 'c':
  232.         if (width <= 0) {
  233.             width = 1;
  234.         }
  235.         case 's':
  236.         if (suppress) {
  237.             while ((width > 0) && (streamChar != EOF)) {
  238.             if (isspace(streamChar) && (formatChar == 's')) {
  239.                 break;
  240.             }
  241.             streamChar = getc(stream);
  242.             width--;
  243.             }
  244.         } else {
  245.             ptr = va_arg(args, char *);
  246.             while ((width > 0) && (streamChar != EOF)) {
  247.             if (isspace(streamChar) && (formatChar == 's')) {
  248.                 break;
  249.             }
  250.             *ptr = streamChar;
  251.             ptr++;
  252.             streamChar = getc(stream);
  253.             width--;
  254.             }
  255.             if (formatChar == 's') {
  256.             *ptr = 0;
  257.             }
  258.             assignedFields++;
  259.         }
  260.         break;
  261.         
  262.         case '[':
  263.         format++; formatChar = *format;
  264.         if (formatChar == '^') {
  265.             format++;
  266.         }
  267.         if (!suppress) {
  268.             firstPtr = ptr = va_arg(args, char *);
  269.         }
  270.         savedPtr = format;
  271.         while ((width > 0) && (streamChar != EOF)) {
  272.             format = savedPtr;
  273.             while (TRUE) {
  274.             if (*format == streamChar) {
  275.                 if (formatChar == '^') {
  276.                 goto stringEnd;
  277.                 } else {
  278.                 break;
  279.                 }
  280.             }
  281.             if ((*format == ']') || (*format == 0)) {
  282.                 if (formatChar == '^') {
  283.                 break;
  284.                 } else {
  285.                 goto stringEnd;
  286.                 }
  287.             }
  288.             format++;
  289.             }
  290.             if (!suppress) {
  291.             *ptr = streamChar;
  292.             ptr++;
  293.             }
  294.             streamChar = getc(stream);
  295.             width--;
  296.         }
  297.         stringEnd:
  298.         if (ptr == firstPtr) {
  299.             goto done;
  300.         }
  301.         while ((*format != ']') && (*format != 0)) {
  302.             format++;
  303.         }
  304.         formatChar = *format;
  305.         if (!suppress) {
  306.             *ptr = 0;
  307.             assignedFields++;
  308.         }
  309.         break;
  310.  
  311.         /*
  312.          * Don't ask why, but for compatibility with UNIX, a null
  313.          * conversion character must always return EOF, and any
  314.          * other conversion character must be treated as decimal.
  315.          */
  316.         
  317.         case 0:
  318.         ungetc(streamChar, stream);
  319.         return(EOF);
  320.         
  321.         default:
  322.         base = 10;
  323.         break;
  324.     }
  325.  
  326.     /*
  327.      * If the field wasn't a number, then everything was handled
  328.      * in the switch statement above.  Otherwise, we still have
  329.      * to read in a number.  This gets handled differently for
  330.      * integers and floating-point numbers.
  331.      */
  332.     
  333.     if (base < 0) {
  334.         continue;
  335.     }
  336.  
  337.     if (streamChar == '-') {
  338.         sign = TRUE;
  339.         width -= 1;
  340.         streamChar = getc(stream);
  341.     } else {
  342.         sign = FALSE;
  343.         if (streamChar == '+') {
  344.         width -= 1;
  345.         streamChar = getc(stream);
  346.         }
  347.     }
  348.  
  349.     /*
  350.      * If we're supposed to be parsing a floating-point number, read
  351.      * the digits into a temporary buffer and use the conversion library
  352.      * routine to convert them.
  353.      */
  354.     
  355. #define COPYCHAR \
  356.     *ptr = streamChar; ptr++; width--; streamChar = getc(stream);
  357.  
  358.     if (base == 0) {
  359.         if (width > MAX_FLOAT_SIZE) {
  360.         width = MAX_FLOAT_SIZE;
  361.         }
  362.         ptr = buf;
  363.         while ((width > 0) && isdigit(streamChar)) {
  364.         COPYCHAR;
  365.         }
  366.         if ((width > 0) && (streamChar == '.')) {
  367.         COPYCHAR;
  368.         }
  369.         while ((width > 0) && isdigit(streamChar)) {
  370.         COPYCHAR;
  371.         }
  372.         if ((width > 0) && ((streamChar == 'e') || (streamChar == 'E'))) {
  373.         COPYCHAR;
  374.         if ((width > 0) &&
  375.             ((streamChar == '+') || (streamChar == '-'))) {
  376.             COPYCHAR;
  377.         }
  378.         while ((width > 0) && isdigit(streamChar)) {
  379.             COPYCHAR;
  380.         }
  381.         }
  382.         *ptr = 0;
  383.  
  384.         if (ptr == buf) {        /* Not a valid number. */
  385.         goto done;
  386.         }
  387.  
  388.         if (!suppress) {
  389.         double d;
  390.         d = atof(buf);
  391.         if (sign) {
  392.             d = -d;
  393.         }
  394.         if (storeLong) {
  395.             *(va_arg(args, double *)) = d;
  396.         } else {
  397.             *(va_arg(args, float *)) = d;
  398.         }
  399.         assignedFields++;
  400.         }
  401.     } else {
  402.         /*
  403.          * This is an integer.  Use special-purpose code for the
  404.          * three supported bases in order to make it run fast.
  405.          */
  406.  
  407.         int i;
  408.         int anyDigits;
  409.  
  410.         i = 0;
  411.         anyDigits = FALSE;
  412.         if (base == 10) {
  413.         while ((width > 0) && isdigit(streamChar)) {
  414.             i = (i * 10) + (streamChar - '0');
  415.             streamChar = getc(stream);
  416.             anyDigits = TRUE;
  417.             width -= 1;
  418.         }
  419.         } else if (base == 8) {
  420.         while ((width > 0) && (streamChar >= '0')
  421.             && (streamChar <= '7')) {
  422.             i = (i << 3) + (streamChar - '0');
  423.             streamChar = getc(stream);
  424.             anyDigits = TRUE;
  425.             width -= 1;
  426.         }
  427.         } else {
  428.         while (width > 0) {
  429.             if (isdigit(streamChar)) {
  430.             i = (i << 4) + (streamChar - '0');
  431.             } else if ((streamChar >= 'a') && (streamChar <= 'f')) {
  432.             i = (i << 4) + (streamChar + 10 - 'a');
  433.             } else if ((streamChar >= 'A') && (streamChar <= 'F')) {
  434.             i = (i << 4) + (streamChar + 10 - 'A');
  435.             } else {
  436.             break;
  437.             }
  438.             streamChar = getc(stream);
  439.             anyDigits = TRUE;
  440.             width--;
  441.         }
  442.         }
  443.         if (!anyDigits) {
  444.         goto done;
  445.         }
  446.         if (sign) {
  447.         i = -i;
  448.         }
  449.         if (!suppress) {
  450.         if (storeShort) {
  451.             *(va_arg(args, short *)) = i;
  452.         } else {
  453.             *(va_arg(args, int *)) = i;
  454.         }
  455.         assignedFields++;
  456.         }
  457.     }
  458.     }
  459.  
  460.     done:
  461.     ungetc(streamChar, stream);
  462.     if ((streamChar == EOF) && (assignedFields == 0)) {
  463.     return(EOF);
  464.     }
  465.     return(assignedFields);
  466. }
  467.